`include "common_header.verilog"

//  *************************************************************************
//  File : p8264_marker_detect.vhd
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2008 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Denis Poverennyy
//  info@morethanip.com
//  *************************************************************************
//  Description : 40G PCS Alignment Marker State Machine
//  Version     : $Id: p8264_marker_detect.v,v 1.5 2017/04/20 18:37:52 wt Exp $
//  *************************************************************************

module p8264_marker_detect (

        reset,
        clk,
        marker_dval_r,
        marker_dval_r2,
        vl_0_enc_in,
        vl_1_enc_in,
        vl_2_enc_in,
        vl_3_enc_in,
        vl_fault,
        data_in,
        sh_in,
        vl_map,
        align_done,
        missed_am,
        align_lost
`ifdef MTIPPCS82_EEE_ENA
        ,
        first_rx_lpi_active,
        ram_period
`endif
                );

input           reset;          //  async active high reset
input           clk;            //  system clock
input           marker_dval_r;  //  Pulse at the expected Alignment Marker position (one clock delayed)
input           marker_dval_r2; //  Pulse at the expected Alignment Marker position (two clocks delayed)
input   [23:0]  vl_0_enc_in;    //  Marker pattern for PCS Virtual Lane 0
input   [23:0]  vl_1_enc_in;    //  Marker pattern for PCS Virtual Lane 1
input   [23:0]  vl_2_enc_in;    //  Marker pattern for PCS Virtual Lane 2
input   [23:0]  vl_3_enc_in;    //  Marker pattern for PCS Virtual Lane 3
input           vl_fault;       //  VL detection failure
input   [63:0]  data_in;        //  Data input
input   [1:0]   sh_in;          //  Sync header
output  [3:0]   vl_map;         //  one hot coding for mapping between virtual lane and physical one
output          align_done;     //  any marker was detected twice
output          missed_am;      //  missed either first or second am.
output          align_lost;     //  alignment lost

`ifdef MTIPPCS82_EEE_ENA 

input           first_rx_lpi_active;    //Boolean variable, first_rx_lpi_active is set true when the receiver is in state RX_LPI_ACTIVE in
                                        // the LPI receive state diagram and R_TYPE(rx_coded) = LI and is otherwise false.
input           ram_period;             // if set the RAM is expected;

`endif

//-------------------------------------
// Output Signals
//-------------------------------------

wire  [3:0]     vl_map;
wire            align_done;
wire            missed_am;
reg             align_lost;

//-------------------------------------
// Internal Signals
//-------------------------------------

wire    [23:0]  vl_0_enc;       // virtual lane 0 number encoding
wire    [23:0]  vl_1_enc;       // virtual lane 1 number encoding 
wire    [23:0]  vl_2_enc;       // virtual lane 2 number encoding 
wire    [23:0]  vl_3_enc;       // virtual lane 3 number encoding 
wire            vl_0_match;     // VL 0 match combinatorial
wire            vl_1_match;     // VL 1 match combinatorial
wire            vl_2_match;     // VL 2 match combinatorial
wire            vl_3_match;     // VL 3 match combinatorial
reg             vl_0_match_r;   // VL 0 match registered 
reg             vl_1_match_r;   // VL 1 match registered 
reg             vl_2_match_r;   // VL 2 match registered 
reg             vl_3_match_r;   // VL 3 match registered
wire    [3:0]   vl_match_num;   // one hot coding for AM number 

reg     [3:0]   am_state;       // AM SM  
reg     [3:0]   next_am_state;  // AM SM next

parameter AM_RESET_CNT  = 4'd 0;
parameter FIND_1ST      = 4'd 1;
parameter TIMER_1       = 4'd 2;
parameter COMP_2ND      = 4'd 3;
parameter TWO_GOOD      = 4'd 4;
parameter AM_SLIP       = 4'd 5;
parameter COMP_AM       = 4'd 6;
parameter GOOD_AM       = 4'd 7;
parameter INVALID_AM    = 4'd 8;
parameter TIMER_2       = 4'd 9;

 
reg             am_valid;       // valid AM detected 
reg     [3:0]   current_am;     // number of the current AM 
reg     [3:0]   first_am;       // number of the first matched AM 
wire            am_match;       // if 1, the first AM and the current AM numbers are the same 
reg     [1:0]   am_invalid_cnt; // Number of the invalid markers received in a row 
wire            am_timer_done;  //  alignment marker timer done
reg             am_lock;        //  alignment marker lock state reached
reg             am_lock_r;      //  one clock delayed am_lock


 `ifdef MTIPPCS82_EEE_ENA
 assign vl_0_enc = (ram_period == 1'b1)? ~ vl_0_enc_in :  vl_0_enc_in; 
 assign vl_1_enc = (ram_period == 1'b1)? ~ vl_1_enc_in :  vl_1_enc_in; 
 assign vl_2_enc = (ram_period == 1'b1)? ~ vl_2_enc_in :  vl_2_enc_in; 
 assign vl_3_enc = (ram_period == 1'b1)? ~ vl_3_enc_in :  vl_3_enc_in; 
 `else 
 assign vl_0_enc = vl_0_enc_in; 
 assign vl_1_enc = vl_1_enc_in; 
 assign vl_2_enc = vl_2_enc_in; 
 assign vl_3_enc = vl_3_enc_in;
 `endif

 

//  Search for VL marker
// 
assign vl_0_match = data_in[55:32] == ~vl_0_enc & data_in[23:0] == vl_0_enc & sh_in == 2'b 01 ? 1'b 1 : 1'b 0; 
assign vl_1_match = data_in[55:32] == ~vl_1_enc & data_in[23:0] == vl_1_enc & sh_in == 2'b 01 ? 1'b 1 : 1'b 0; 
assign vl_2_match = data_in[55:32] == ~vl_2_enc & data_in[23:0] == vl_2_enc & sh_in == 2'b 01 ? 1'b 1 : 1'b 0; 
assign vl_3_match = data_in[55:32] == ~vl_3_enc & data_in[23:0] == vl_3_enc & sh_in == 2'b 01 ? 1'b 1 : 1'b 0; 


always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                vl_0_match_r <= 1'b 0;	
                vl_1_match_r <= 1'b 0;	
                vl_2_match_r <= 1'b 0;	
                vl_3_match_r <= 1'b 0;	
        end
        else
        begin		
                vl_0_match_r <= vl_0_match;	
                vl_1_match_r <= vl_1_match;	
                vl_2_match_r <= vl_2_match;	
                vl_3_match_r <= vl_3_match;	
        end
end

assign vl_match_num = {vl_3_match_r, vl_2_match_r, vl_1_match_r, vl_0_match_r}; 


// Current AM
always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                am_valid <= 1'b 0;	
                current_am <= {4{1'b 0}};	
                end
        else
        begin
                if (marker_dval_r == 1'b 1)
                begin
                        am_valid <= vl_0_match_r | vl_1_match_r | vl_2_match_r | vl_3_match_r;	
                        current_am <= vl_match_num;	
                end
                else
                begin
                        am_valid <= 1'b 0;	
                        current_am <= {4{1'b 0}};	
                end
        end
end

// First AM
always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                first_am <= {4{1'b 0}};	
        end
        else
        begin
                if (vl_fault == 1'b 1 | am_state == AM_SLIP)
                begin
                        first_am <= {4{1'b 0}};	
                end
                else if (am_state == FIND_1ST & am_valid == 1'b 1 )
                begin
                        first_am <= current_am;	
                end
        end
end

assign am_match = current_am == first_am ? 1'b 1 : 1'b 0; 

// Number of invalid AM
always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                am_invalid_cnt <= {2{1'b 0}};	
        end
        else
        begin
                if (am_state == AM_RESET_CNT | am_state == GOOD_AM)
                begin
                        am_invalid_cnt <= {2{1'b 0}};	
                end
                else if (am_state == INVALID_AM )
                begin
                        am_invalid_cnt <= am_invalid_cnt + 2'b 01;	
                end
        end
end


//  Aligment marker lock state machine
// 

always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                am_state <= AM_RESET_CNT;	
        end
        else
        begin
                if (vl_fault == 1'b 1)
                begin
                        am_state <= AM_RESET_CNT;	
                end
                else
                begin
                        am_state <= next_am_state;	
                end
        end
end

always @(am_state or am_valid or am_timer_done or am_match or am_invalid_cnt

`ifdef MTIPPCS82_EEE_ENA 
        or first_rx_lpi_active
`endif 
)
begin
        case (am_state)
        AM_RESET_CNT:
                begin
                        next_am_state = FIND_1ST;	
                end
        FIND_1ST:
               begin
                        if (am_valid == 1'b 1)
                        begin
                                next_am_state = TIMER_1;	
                        end
                        else
                        begin
                                next_am_state = FIND_1ST;	
                        end
               end
        TIMER_1:
                begin
                        if (am_timer_done == 1'b 1)
                        begin
                                next_am_state = COMP_2ND;	
                        end
                        else
                        begin
                                next_am_state = TIMER_1;	
                        end
                end
        COMP_2ND:
                begin
                        if (am_match == 1'b 1)
                        begin
                                next_am_state = TWO_GOOD;	
                        end
                        else
                        begin
                                next_am_state = AM_SLIP;	
                        end
                end
        TWO_GOOD:
                begin
                        next_am_state = TIMER_2;	
                end
        TIMER_2:
                begin
        
                `ifdef MTIPPCS82_EEE_ENA 
                        if (first_rx_lpi_active == 1'b1)
                        begin
                                next_am_state = FIND_1ST;         
                        end
                        else
                `endif            
                        if (am_timer_done == 1'b 1)
                        begin
                                next_am_state = COMP_AM;	
                        end
                        else
                        begin
                                next_am_state = TIMER_2;	
                        end
                end
        COMP_AM:
                begin
                        if (am_match == 1'b 1)
                        begin
                                next_am_state = GOOD_AM;	
                        end
                        else
                        begin
                                next_am_state = INVALID_AM;	
                        end
                end
        GOOD_AM:
                begin
                        next_am_state = TIMER_2;	
                end
        INVALID_AM:
                begin
                        if (am_invalid_cnt == 2'b 11)
                        begin
                                next_am_state = AM_SLIP;	
                        end
                        else
                        begin
                                next_am_state = TIMER_2;	
                        end
                end
        AM_SLIP:
                begin
                        next_am_state = AM_RESET_CNT;	
                end
        default:
                begin
                        next_am_state = AM_RESET_CNT;	
                end
        endcase
end

assign am_timer_done = marker_dval_r; 


// if two good AM received => AM done
always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                am_lock <= 1'b 0;	
        end
        else
        begin
                if (vl_fault == 1'b 1 || am_state == AM_SLIP)
                begin
                        am_lock <= 1'b 0;		
                end
                else if (am_state == TWO_GOOD )
                begin
                        am_lock <= 1'b 1;		
                end
        end
end

assign   vl_map = first_am;
assign align_done = am_lock; 

// Align lost generation
always @(posedge clk or posedge reset)
begin
        if (reset == 1'b 1)
        begin
                align_lost <= 1'b 0;	
                am_lock_r <= 1'b 0;	
        end
        else
        begin
                am_lock_r <= am_lock;	
                align_lost <= ~am_lock & am_lock_r;	
        end
end

assign missed_am = marker_dval_r2 == 1'b 1 & am_lock == 1'b 0 & am_valid == 1'b 0 ? 1'b 1 : 1'b 0; 

endmodule // module p8264_marker_detect